#include "fifo.h"
#include <stdlib.h>
#include <string.h>

Fifo::Fifo(DWORD capacity)
{
    m_capacity = capacity;
    m_bBuffer = new BYTE[m_capacity];
    m_iPop = m_iPush = 0;
}

Fifo::~Fifo()
{
    delete[] m_bBuffer;
}

Ufifo::Ufifo(int units, DWORD unitSize) : Fifo(units * unitSize)
{
    m_unitSize = unitSize;
}

DWORD Ufifo::GetUnitCount(void)
{
    if (m_iPush >= m_iPop) {
        return((m_iPush - m_iPop) / m_unitSize);
    }
    else {
        return((m_capacity + m_iPush - m_iPop) / m_unitSize);
    }
}

BOOL Ufifo::PushUnit(const void* srcUnit)
{
    DWORD iPopLocked = m_iPop;			// 实际iPop可能后退，不影响现在锁定可写空间
    if (m_iPush >= iPopLocked) {
        // ....iPopLocked====iPush[写在右侧]..
        if (m_unitSize < m_capacity - m_iPush) {
            memcpy(m_bBuffer + m_iPush, srcUnit, m_unitSize);
            m_iPush += m_unitSize;
            return(TRUE);
        }
            // ....iPopLocked====iPush[写在最右端]
        else if (m_unitSize == m_capacity - m_iPush) {
            if (iPopLocked == 0) {		// 避免iPush==iPop，写满的歧义
                return(FALSE);
            }
            else {
                memcpy(m_bBuffer + m_iPush, srcUnit, m_unitSize);
                m_iPush = 0;
                return(TRUE);
            }
        }
            // [写在最左端]....iPopLocked====，保留间隙
        else if (m_unitSize < iPopLocked) {
            memcpy(m_bBuffer, srcUnit, m_unitSize);
            m_iPush = m_unitSize;
            return(TRUE);
        }
        else {
            return(FALSE);
        }
    }
    else {
        // ===iPush[写在中间]....iPopLocked====，保留间隙
        if (m_unitSize < iPopLocked - m_iPush) {
            memcpy(m_bBuffer + m_iPush, srcUnit, m_unitSize);
            m_iPush += m_unitSize;
            return(TRUE);
        }
        else {
            return(FALSE);
        }
    }
}

DWORD Ufifo::PopUnit(void* dstUnitBuf)
{
    DWORD iPushLocked = m_iPush;		// 实际iPush可能后退，不影响现在锁定可读数据段
    if (m_iPop == iPushLocked) {		// 因为Push的时候避免全满，所以只能判断为全空
        return(0);
    }

    memcpy(dstUnitBuf, m_bBuffer + m_iPop, m_unitSize);
    if (m_iPop + m_unitSize == m_capacity) {
        m_iPop = 0;
    }
    else {
        m_iPop += m_unitSize;
    }
    return(m_unitSize);
}

DWORD Ufifo::PushUnits(const void* srcUcache, DWORD totalLen)
{
    DWORD iPopLocked = m_iPop;			// 实际iPop可能后退，不影响现在锁定可写空间
    DWORD freeLen, pushingLen;
    if (m_iPush >= iPopLocked) {
        // 两侧加起来的自由空间
        freeLen = (m_capacity - m_iPush) + iPopLocked;
        if (freeLen <= m_unitSize) {
            return(0);
        }

        pushingLen = __min(totalLen, freeLen - m_unitSize);
        DWORD rightSize = m_capacity - m_iPush;
        // 右侧足够
        if(pushingLen < rightSize) {
            memcpy(m_bBuffer + m_iPush, srcUcache, pushingLen);
            m_iPush += pushingLen;
        }
            // 右侧刚够
        else if (pushingLen == rightSize) {
            memcpy(m_bBuffer + m_iPush, srcUcache, pushingLen);
            m_iPush = 0;
        }
            // 需要左侧
        else {
            memcpy(m_bBuffer + m_iPush, srcUcache, rightSize);
            memcpy(m_bBuffer, (BYTE*)srcUcache + rightSize, pushingLen - rightSize);
            m_iPush = pushingLen - rightSize;
        }
    }
    else {
        freeLen = iPopLocked - m_iPush;
        if (freeLen <= m_unitSize) {
            return(0);
        }

        pushingLen = __min(totalLen, freeLen - m_unitSize);
        memcpy(m_bBuffer + m_iPush, srcUcache, pushingLen);
        m_iPush += pushingLen;
    }
    return(pushingLen);
}

DWORD Ufifo::PopUnits(void* dstUcache, DWORD cacheSize)
{
    DWORD iPushLocked = m_iPush;		// 实际iPush可能后退，不影响现在锁定可读数据段
    cacheSize = cacheSize / m_unitSize * m_unitSize;
    DWORD dataLen, poppingLen;
    // 连续一段数据
    if (m_iPop < iPushLocked) {
        dataLen = iPushLocked - m_iPop;
        poppingLen = __min(dataLen, cacheSize);
        memcpy(dstUcache, m_bBuffer + m_iPop, poppingLen);
        m_iPop += poppingLen;
    }
        // 空
    else if (m_iPop == iPushLocked) {
        dataLen = 0;
        poppingLen = 0;
    }
        // 至多两段数据，右段+可能左段
    else {
        DWORD rightDataLen = m_capacity - m_iPop;
        DWORD rightPoppingLen = __min(rightDataLen, cacheSize);
        dataLen = rightDataLen + iPushLocked;
        poppingLen = __min(dataLen, cacheSize);
        // 先读出右段数据
        memcpy(dstUcache, m_bBuffer + m_iPop, rightPoppingLen);
        if (rightPoppingLen < rightDataLen) {
            m_iPop += rightPoppingLen;
        }
        else if (rightPoppingLen == rightDataLen) {
            m_iPop = 0;
            // 右段读光，继续读左段数据
            if (rightPoppingLen < poppingLen) {
                DWORD leftPoppingLen = poppingLen - rightPoppingLen;
                memcpy((BYTE*)dstUcache + rightPoppingLen, m_bBuffer, leftPoppingLen);
                m_iPop = leftPoppingLen;
            }
        }
    }
    return(poppingLen);
}

DWORD Ufifo::PopAsMany(Ufifo* dstUfifo)
{
    DWORD iPushLocked = m_iPush;		// 实际iPush可能后退，不影响现在锁定可读数据段
    DWORD dataLen, poppedLen;
    // 连续一段数据
    if (m_iPop < iPushLocked) {
        dataLen = iPushLocked - m_iPop;
        poppedLen = dstUfifo->PushUnits(m_bBuffer + m_iPop, dataLen);
        m_iPop += poppedLen;
    }
        // 空
    else if (m_iPop == iPushLocked) {
        dataLen = 0;
        poppedLen = 0;
    }
        // 至多两段数据，右段+可能左段
    else {
        DWORD rightDataLen = m_capacity - m_iPop;
        // 先读出右段数据
        poppedLen = dstUfifo->PushUnits(m_bBuffer + m_iPop, rightDataLen);
        if (poppedLen < rightDataLen) {
            m_iPop += poppedLen;
        }
        else if (poppedLen == rightDataLen) {
            m_iPop = 0;
            // 右段读光，继续读左段数据
            if (iPushLocked > 0) {
                DWORD leftPoppedLen = dstUfifo->PushUnits(m_bBuffer, iPushLocked);
                poppedLen += leftPoppedLen;
                m_iPop = leftPoppedLen;
            }
        }
    }

    return(poppedLen);
}

CxUfifo::CxUfifo(int units, DWORD unitSize) : Ufifo(units, unitSize)
{
    m_cs = new CxSection();
}

CxUfifo::~CxUfifo()
{
    delete m_cs;
}

BOOL CxUfifo::PushUnit(const void* srcUnit)
{
    m_cs->Enter();
    BOOL pushOk = Ufifo::PushUnit(srcUnit);
    m_cs->Leave();
    return(pushOk);
}

DWORD CxUfifo::PopUnit(void* dstUnitBuf)
{
    m_cs->Enter();
    DWORD n = Ufifo::PopUnit(dstUnitBuf);
    m_cs->Leave();
    return(n);
}

DWORD CxUfifo::PushUnits(const void* srcUcache, DWORD totalLen)
{
    m_cs->Enter();
    DWORD n = Ufifo::PushUnits(srcUcache, totalLen);
    m_cs->Leave();
    return(n);
}

DWORD CxUfifo::PopUnits(void* dstUcache, DWORD cacheSize)
{
    m_cs->Enter();
    DWORD n = Ufifo::PopUnits(dstUcache, cacheSize);
    m_cs->Leave();
    return(n);
}

DWORD CxUfifo::PopAsMany(Ufifo* dstUfifo)
{
    m_cs->Enter();
    DWORD n = Ufifo::PopAsMany(dstUfifo);
    m_cs->Leave();
    return(n);
}

// 一个生产端线程专门Push，先锁定iPop相当于锁定可写空间，确定成功写入数据后后移iPush
BOOL Nfifo::PushNtape(const void* srcNtape)
{
    DWORD tapeLen = NLENGTH(srcNtape);
    if (tapeLen == 0) {
        return(FALSE);
    }
    DWORD iPopLocked = m_iPop;			// 实际iPop可能后退，不影响现在锁定可写空间
    if (m_iPush >= iPopLocked) {
        // ....iPopLocked====iPush[写在右侧]....保留右端至少4字节0
        if (tapeLen + 4 <= m_capacity - m_iPush) {
            memcpy(m_bBuffer + m_iPush, srcNtape, tapeLen);
            m_iPush += tapeLen;
            return(TRUE);
        }
            // [写在最左端]....iPopLocked====0000，保留至少4字节间隙
        else if (tapeLen + 4 <= iPopLocked) {
            // 右端填充0
            memset(m_bBuffer + m_iPush, 0x00, m_capacity - m_iPush);
            memcpy(m_bBuffer, srcNtape, tapeLen);
            m_iPush = tapeLen;
            return(TRUE);
        }
        else {
            return(FALSE);
        }
    }
    else {
        // ===iPush[写在中间]....iPopLocked====0000，保留至少4字节间隙
        if (tapeLen + 4 <= iPopLocked - m_iPush) {
            memcpy(m_bBuffer + m_iPush, srcNtape, tapeLen);
            m_iPush += tapeLen;
            return(TRUE);
        }
        else {
            return(FALSE);
        }
    }
}

// 一个消费端线程专门Pop，先锁定iPush相当于锁定可读空间，确定成功读出数据后后移iPop
DWORD Nfifo::PopNtape(void* dstNbuf, DWORD bufSize)
{
    DWORD iPushLocked = m_iPush;		// 实际iPush可能后退，不影响现在锁定可读数据段
    if (m_iPop == iPushLocked) {		// 因为Push的时候避免全满，所以只能判断为全空
        return(0);
    }

    BYTE* srcTape = m_bBuffer + m_iPop;
    DWORD srcLen = NLENGTH(srcTape);
    if (srcLen == 0) {					// 遇到右端填0段，绕回左端
        m_iPop = 0;
        if (m_iPop == iPushLocked) {
            return(0);
        }
        srcTape = m_bBuffer;
        srcLen = NLENGTH(srcTape);
    }
    if (srcLen > bufSize) {
        return(0);
    }
    memcpy(dstNbuf, srcTape, srcLen);
    m_iPop += srcLen;
    return(srcLen);
}

DWORD Nfifo::PushNtapes(const void* srcNcache, DWORD totalLen)
{
    DWORD pushedTotalLen = 0;
    BYTE* nextSrc = (BYTE*)srcNcache;
    DWORD nextLen = NLENGTH(nextSrc);
    while ((nextLen != 0) && (pushedTotalLen + nextLen <= totalLen)) {
        if (!PushNtape(nextSrc)) {
            break;
        }

        pushedTotalLen += nextLen;
        nextSrc += nextLen;
        if (pushedTotalLen == totalLen) {
            break;
        }
        else {
            nextLen = NLENGTH(nextSrc);
        }
    }
    return(pushedTotalLen);
}

DWORD Nfifo::PopNtapes(void* dstNcache, DWORD cacheSize)
{
    DWORD poppedTotalLen = 0;
    BYTE* nextDst = (BYTE*)dstNcache;
    DWORD leftLen = cacheSize;
    while (leftLen > 0) {
        DWORD popped = PopNtape(nextDst, leftLen);
        if(popped == 0) {
            break;
        }

        poppedTotalLen += popped;
        leftLen -= popped;
        nextDst += popped;
    }
    return(poppedTotalLen);
}

DWORD Nfifo::PopAsMany(Nfifo* dstNfifo)
{
    DWORD iPushLocked = m_iPush;		// 实际iPush可能后退，不影响现在锁定可读数据段
    DWORD poppedTotalLen = 0;
    while (m_iPop != iPushLocked) {
        BYTE* srcTape = m_bBuffer + m_iPop;
        DWORD srcLen = NLENGTH(srcTape);
        if (srcLen == 0) {
            m_iPop = 0;
            continue;
        }
        if (!dstNfifo->PushNtape(srcTape)) {
            break;
        }
        m_iPop += srcLen;
        poppedTotalLen += srcLen;
    }
    return(poppedTotalLen);
}

CxNfifo::CxNfifo(DWORD capacity): Nfifo(capacity)
{
    m_cs = new CxSection();
}

CxNfifo::~CxNfifo()
{
    delete m_cs;
}

BOOL CxNfifo::PushNtape(void* ntape)
{
    m_cs->Enter();
    BOOL pushOk = Nfifo::PushNtape(ntape);
    m_cs->Leave();
    return(pushOk);
}

WORD CxNfifo::PeekNextLength(void)
{
    WORD len;
    m_cs->Enter();
    if(m_iPop == m_iPush) {
        len = 0;
    }
    else {
        len = NLENGTH(m_bBuffer+m_iPop);
        if (len == 0) {					// 遇到填0折回
            m_iPop = 0;
            if (m_iPop == m_iPush) {
                len = 0;
            }
            else {
                len = NLENGTH(m_bBuffer);
            }
        }
    }
    m_cs->Leave();
    return(len);
}

WORD CxNfifo::PeekNextType(void)
{
    WORD type;
    m_cs->Enter();
    if (m_iPush == m_iPop) {
        type = 0;
    }
    else {
        type = NTYPE(m_bBuffer + m_iPop);
        if (type == 0) {				// 遇到填0折回
            m_iPop = 0;
            if (m_iPush == m_iPop) {
                type = 0;
            }
            else {
                type = NTYPE(m_bBuffer);
            }
        }
    }
    m_cs->Leave();
    return(type);
}

DWORD CxNfifo::PopNtape(void* dstNbuf, DWORD bufSize)
{
    m_cs->Enter();
    DWORD n = Nfifo::PopNtape(dstNbuf, bufSize);
    m_cs->Leave();
    return(n);
}

DWORD CxNfifo::PushNtapes(const void* srcNcache, DWORD totalLen)
{
    m_cs->Enter();
    DWORD n = Nfifo::PushNtapes(srcNcache, totalLen);
    m_cs->Leave();
    return(n);
}

DWORD CxNfifo::PopNtapes(void* dstNcache, DWORD cacheSize)
{
    m_cs->Enter();
    DWORD n = Nfifo::PopNtapes(dstNcache, cacheSize);
    m_cs->Leave();
    return(n);
}

DWORD CxNfifo::PopAsMany(Nfifo* dstNfifo)
{
    m_cs->Enter();
    DWORD n = Nfifo::PopAsMany(dstNfifo);
    m_cs->Leave();
    return(n);
}
